/**
 * main.c - a simple command line tool for audio files.
 * @author  Patrick J Leyden III
 * @version 0.1
 *
 * Plan:
 * Input Data: iiiiiiiiiiiiiii (len: 15)
 * Window: {   } (size: 3)
 * Window Offset: --> (offset: 2)
 *
 * Iterations:
 * 1:
 *        {   }
 *          iiiiiiiiiiiiiii
 *
 * 2:
 *        {   } -->
 *          {   }
 *          iiiiiiiiiiiiiii
 *
 * 3:
 *          {   } -->
 *            {   }
 *          iiiiiiiiiiiiiii
 * 4:
 *            {   } -->
 *              {   }
 *          iiiiiiiiiiiiiii
 * N:
 *                      {   } -->
 *                        {   }
 *          iiiiiiiiiiiiiii
 */

#include <stdio.h>
#include <sndfile.h>
#include <stdlib.h>

#define EXIT_CODE_SUCCESS 0
#define EXIT_CODE_BAD_FILENAME -1
#define EXIT_CODE_UNEXPECTED_ERROR -2
#define MAX_FORMAT_STRING_LENGTH 80

void print_file_info(const SF_INFO * p_info);
char * malloc_format_string(int format);
const char * endian_format_string(int format);
const char * type_format_string(int format);
const char * subtype_format_string(int format);

/**
 * Runs the main program.
 * @param argc  the number of arguments
 * @param argv  an array of null terminated strings.
 */
int main(int argc, char ** argv)
{
    SNDFILE * sfp;
    /* Setting the SF_INFO struct values to all zeros is important. */
    SF_INFO info = {0, 0, 0, 0, 0, 0};
    const char * filename = "voice.wav";
    int * p_buffer;
    const sf_count_t num_frames = 100;
    sf_count_t num_read;
    FILE * fp;

    sfp = sf_open(filename, SFM_READ, &info);
    if (NULL == sfp) {
        fprintf(stderr, "error: failed to open %s\n", filename);
        exit(EXIT_CODE_BAD_FILENAME);
    }

    print_file_info(&info);

    /* For now, print data to file. */
    fp = fopen("plaudex.out", "wb");
    if (NULL == fp) {
        fprintf(stderr, "error: cannot open file for writing");
        sf_close(sfp);
        exit(EXIT_CODE_UNEXPECTED_ERROR);
    }

    p_buffer = (int *)malloc(num_frames * info.channels * sizeof(int));
    do {
        num_read = sf_readf_int(sfp, p_buffer, num_frames);
        if (num_read > 0) {
            /* TODO: add "real" code here. */
            int * ch_buff = (int *)malloc(num_read * sizeof(int));
            int i;
            int ch_offset = 0;
            for (i = 0; i < num_read; ++i) {
                ch_buff[i] = p_buffer[(i * info.channels) + ch_offset];
                fprintf(fp, "%d\n", ch_buff[i]);
            }
            /*fwrite((const char *)ch_buff, sizeof(int), num_read, fp);*/
            free((void *)ch_buff);
        }
    } while (num_read > 0);

    free((void *)p_buffer);

    fclose(fp);

    sf_close(sfp);

    return EXIT_CODE_SUCCESS;
}

/** print_file_info function comments go here.
 */
void print_file_info(const SF_INFO * p_info)
{
    if (NULL != p_info) {
        char * format_string = malloc_format_string(p_info->format);
        printf("frames: %d\n"
               "samplerate: %d\n"
               "channels: %d\n"
               "format: %s\n", (int)p_info->frames, p_info->samplerate,
               p_info->channels, format_string);
        free((void *)format_string);
    }
}

char * malloc_format_string(int format)
{
    char * str = (char *)malloc(MAX_FORMAT_STRING_LENGTH);
    snprintf(str, MAX_FORMAT_STRING_LENGTH, "[%s | %s | %s]",
        endian_format_string(format), type_format_string(format),
        subtype_format_string(format));

    return str;
}

const char * endian_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_ENDMASK) {
        case SF_ENDIAN_FILE:
            str = "Default Endian";
            break;
        case SF_ENDIAN_LITTLE:
            str = "Little Endian";
            break;
        case SF_ENDIAN_BIG:
            str = "Big Endian";
            break;
        case SF_ENDIAN_CPU:
            str = "CPU Endian";
            break;
        default:
            str = "Unknown Endian";
            break;
    }
    return str;
}

const char * type_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_TYPEMASK) {
        case SF_FORMAT_WAV:
            str = "WAV";
            break;
        case SF_FORMAT_AIFF:
            str = "AIFF";
            break;
        case SF_FORMAT_AU:
            str = "AU";
            break;
        /* TODO: Continue adding types. */
        default:
            str = "Unknown Type";
            break;
    }
    return str;
}

const char * subtype_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_SUBMASK) {
        default:
            str = "Unknown Subtype";
            break;
    }
    return str;
}
